package com.cryptape.cita_wallet.service;

import android.util.Log;

import com.alm.token.MyApp;
import com.cryptape.cita_wallet.constant.ConstantUtil;
import com.cryptape.cita_wallet.exception.BizException;
import com.cryptape.cita_wallet.service.http.EthRpcService;
import com.cryptape.cita_wallet.util.db.DBWalletUtil;

import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.DynamicArray;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Uint;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthCall;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutionException;

/**
 * Created by dengming on 14/1/20.
 */

public class ContractService {

    public static Map<String, String> roundDetail() throws Exception {
        String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;

        List<Type> inputParam = new ArrayList<>();

        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<Uint256>() {
        });
        outputParam.add(new TypeReference<Uint256>() {
        });
        outputParam.add(new TypeReference<Uint256>() {
        });
        outputParam.add(new TypeReference<Uint256>() {
        });
        outputParam.add(new TypeReference<Uint>() {
        });
        outputParam.add(new TypeReference<Uint256>() {
        });
        outputParam.add(new TypeReference<Uint256>() {
        });
        outputParam.add(new TypeReference<Uint>() {
        });
        Function function = new Function("roundDetail", inputParam, outputParam);
        EthCall call = request(ConstantUtil.getExchangeContract(), function);
        if (call.getError() == null) {
            List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
//            Log.e("MainActivity",""+result.get(0).getValue().toString());
            Map<String, String> data = new HashMap<>();
            data.put("maxRound", result.get(0).getValue().toString());
            data.put("round", result.get(1).getValue().toString());
            data.put("rate", result.get(2).getValue().toString());
            data.put("residueToken", result.get(3).getValue().toString());
            data.put("cap", result.get(4).getValue().toString());
            data.put("nextRound", result.get(5).getValue().toString());
            data.put("nextRate", result.get(6).getValue().toString());
            data.put("nextCap", result.get(7).getValue().toString());
            return data;
        }
        return null;
    }


    private static EthCall request0(String contractAddress, Function function) throws InterruptedException, ExecutionException {
        String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
        Transaction transaction = Transaction.createEthCallTransaction(address, contractAddress,
                FunctionEncoder.encode(function));
        EthCall request = null;
        {
            request = EthRpcService.service.ethCall(transaction, DefaultBlockParameterName.LATEST).sendAsync().get();
        }
        Log.e("MainActivity", request.getValue() + " " + request.getJsonrpc()
        );
        return request;
    }

    private static EthCall request(String contractAddress, Function function) throws InterruptedException, ExecutionException {
        String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
        Transaction transaction = Transaction.createEthCallTransaction(address, contractAddress,
                FunctionEncoder.encode(function));
//        Transaction transaction = Transaction.createEthCallTransaction(address,"0xCB00A25BDf7B58995554e852A9D820cE9C49C5B8",
//                FunctionEncoder.encode(function));
        EthCall request = null;
//        if(function.getName().equals("lockNature")){
//            request = EthRpcService.service.ethSendTransaction(transaction).sendAsync().get();
//        }else
        {
            request = EthRpcService.service.ethCall(transaction, DefaultBlockParameterName.LATEST).sendAsync().get();
        }
//        Log.e("MainActivity", request.getValue() + " " + request.getJsonrpc()
//        );
        return request;
    }

    public static List<Type> queryMember() throws Exception {
        String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;

        List<Type> inputParam = new ArrayList<>();
        inputParam.add(new Address(address));

        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<Address>() {
        });
        outputParam.add(new TypeReference<Utf8String>() {
        });
        outputParam.add(new TypeReference<Address>() {
        });
        outputParam.add(new TypeReference<Uint>() {
        });
        outputParam.add(new TypeReference<Uint>() {
        });
        Function function = new Function("queryMember", inputParam, outputParam);
        EthCall call = request(ConstantUtil.getRELATIONContract(), function);
        if (call.getError() == null) {
            List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
//            Log.e("MainActivity",""+result.get(0).getValue().toString());
//            Map<String,String> data = new HashMap<>();
//            data.put("maxRound",result.get(0).getValue().toString());
//            data.put("round" ,result.get(1).getValue().toString());
//            data.put("rate",result.get(2).getValue().toString());
//            data.put("residueToken",result.get(3).getValue().toString());
//            data.put("cap",result.get(4).getValue().toString());
//            data.put("nextRound",result.get(5).getValue().toString());
//            data.put("nextRate",result.get(6).getValue().toString());
//            data.put("nextCap",result.get(7).getValue().toString());
            return result;
        }
        return null;
    }

    public static boolean checkMemberExistsAddress() throws InterruptedException, ExecutionException {
        // Input parameters
        String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
        List<Type> inputParam = new ArrayList<>();
        inputParam.add(new Address(address));
        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<Bool>() {
        });

        Function function = new Function("checkMemberExistsAddress", inputParam, outputParam);
        EthCall call = request(ConstantUtil.getRELATIONContract(), function);

        if (call.getError() == null) {
            List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
            Log.e("MainActivity", "" + result.get(0).getValue().toString());
            return ((Boolean) result.get(0).getValue()).booleanValue();
        }
        return false;
    }

    public static boolean checkMemberExistsMid(String mid) {
        List<Type> inputParam = new ArrayList<>();
        inputParam.add(new Utf8String(mid));

        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<Bool>() {
        });
        Function function = new Function("checkMemberExistsMid", inputParam, outputParam);
        try {
            EthCall call = request(ConstantUtil.getRELATIONContract(), function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
//                Log.e("MainActivity",""+result.get(0).getValue().toString());
                if (result == null || result.size() == 0) {
                    return false;
                }
                return ((Boolean) result.get(0).getValue()).booleanValue();
            }
            return false;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return false;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return false;
        }
    }

//    protected org.web3j.abi.datatypes.DynamicArray getDynArray(List<BigInteger> indices)
//    {
//        new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.generated.Uint256>(
//                org.web3j.abi.datatypes.generated.Uint256.class,
//                org.web3j.abi.Utils.typeMap(null, org.web3j.abi.datatypes.generated.Uint256.class))),
////        return new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.generated.Uint256>(
////                org.web3j.abi.datatypes.generated.Uint256.class);
//    }

    public static List<Type> memberBuyed() {
        List<Type> inputParam = new ArrayList<>();
        String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
        inputParam.add(new Utf8String(address));
        //        inputParam.add(new Uint256(1));
        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<DynamicArray<Uint256>>(){});
        Function function = new Function("memberBuyed", inputParam, outputParam);
        try {
            EthCall call = request(ConstantUtil.getMinerSellContract(), function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
                if (result == null || result.size() == 0) {
                    Log.e("MainActivity", "miner return is null");
                    return null;
                }
                return result;
            }
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static List<Type> minerDetail() throws BizException {
        List<Type> inputParam = new ArrayList<>();
//        inputParam.add(new Uint256(1));
        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<DynamicArray<Uint256>>(){});
        outputParam.add(new TypeReference<DynamicArray<Uint256>>(){});
        outputParam.add(new TypeReference<DynamicArray<Uint256>>(){});
        Function function = new Function("minerDetail", inputParam, outputParam);
        try {
            EthCall call = request(ConstantUtil.getMinerSellContract(), function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
                if (result == null || result.size() == 0) {
                    Log.e("MainActivity", "miner return is null");
                    return null;
                }
                return result;
            }
            throw new BizException();
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new BizException();
        } catch (ExecutionException e) {
            e.printStackTrace();
            throw new BizException();
        }
    }

    public static byte[] buyBytes(int type){
//        String mid = "U0";

        List<Type> inputParam = new ArrayList<>();
        inputParam.add(new Uint256(type));

        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<org.web3j.abi.datatypes.DynamicBytes>() {
        });
        Function function = new Function("buyBytes", inputParam, outputParam);
        try {
            EthCall call = request(ConstantUtil.getMinerSellContract(), function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
                if(result != null && result.size() > 0) {
                    Log.e("MainActivity", "buyBytes:" + result.get(0).getValue().toString());
                    return ((byte[]) result.get(0).getValue());
                }
            }
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static byte[] registerBytes(String midCode) {
        String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
//        String mid = "U0";
        String random = "M" + getRandom(8);

        List<Type> inputParam = new ArrayList<>();
        inputParam.add(new Address(address));
        inputParam.add(new Utf8String(midCode));
        inputParam.add(new Utf8String(random));

        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<org.web3j.abi.datatypes.DynamicBytes>() {
        });
        Function function = new Function("registerBytes", inputParam, outputParam);
        try {
            EthCall call = request(ConstantUtil.getRegisterContract(), function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
                if(result != null && result.size() > 0) {
                    Log.e("MainActivity", "registerBytes:" + result.get(0).getValue().toString());
                    return ((byte[]) result.get(0).getValue());
                }
            }
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String getRandom(int length) {
        String val = "";
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            val += String.valueOf(random.nextInt(10));
        }
        return val;
    }

    public static boolean queryMember(String mid) {
        String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
        List<Type> inputParam = new ArrayList<>();
        inputParam.add(new Utf8String(mid));
        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<Bool>() {
        });
        Function function = new Function("checkMemberExistsMid", inputParam, outputParam);
        try {
            EthCall call = request(ConstantUtil.getRELATIONContract(), function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
                Log.e("MainActivity", "" + result.get(0).getValue().toString());
                return ((Boolean) result.get(0).getValue()).booleanValue();
            }
            return false;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return false;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return false;
        }
    }

    public static List<Type> lockbalance(String contractAddress) {
        List<Type> inputParam = new ArrayList<>();
        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<Uint256>() {
        });
        Function function = new Function("lockBalance", inputParam, outputParam);
        try {
            EthCall call = request(contractAddress, function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
//                Log.e("MainActivity",""+result.get(0).getValue().toString());
                return result;
            }
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static List<Type> lockAddress() {
        List<Type> inputParam = new ArrayList<>();
        String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
        inputParam.add(new Address(address));
        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<Address>() {
        });
//        outputParam.add(new TypeReference<Utf8String>(){});
//        outputParam.add(new TypeReference<Uint256>(){});
        Function function = new Function("lockAddress", inputParam, outputParam);
        try {
            EthCall call = request(ConstantUtil.DEPOSIT_CONTRACT, function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
//                Log.e("MainActivity",""+result.get(0).getValue().toString());
                return result;
            }
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static List<Type> orderDetail(String contractAddress) {
        List<Type> inputParam = new ArrayList<>();
        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<Uint256>() {
        });
        outputParam.add(new TypeReference<Uint256>() {
        });
        outputParam.add(new TypeReference<Uint256>() {
        });
        Function function = new Function("orderDetail", inputParam, outputParam);
        try {
            EthCall call = request(contractAddress, function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
//                Log.e("MainActivity",""+result.get(0).getValue().toString());
                return result;
            }
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static List<Type> incentiveDetail(String contractAddress) {
        List<Type> inputParam = new ArrayList<>();
        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<DynamicArray<Uint256>>() {});
//        outputParam.add(new TypeReference<Uint256>() {
//        });
//        outputParam.add(new TypeReference<Uint256>() {
//        });
        Function function = new Function("incentiveDetail", inputParam, outputParam);
        try {
            EthCall call = request(contractAddress, function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
                if (result == null || result.size() == 0) {
                    Log.e("MainActivity", "incentiveDetail return is null");
                    return null;
                }
                return result;
            }
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static List<Type> queryMixDetail() {
        List<Type> inputParam = new ArrayList<>();
        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<Uint256>() {
        });
        outputParam.add(new TypeReference<Uint256>() {
        });
        outputParam.add(new TypeReference<Uint256>() {
        });
        Function function = new Function("queryMixDetail", inputParam, outputParam);
        try {
            EthCall call = request(ConstantUtil.DEPOSIT_CONTRACT, function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
//                Log.e("MainActivity",""+result.get(0).getValue().toString());
                return result;
            }
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static List<Type> bTokenBalance() {
        List<Type> inputParam = new ArrayList<>();
        String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
        inputParam.add(new Address(address));
        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<Uint256>() {
        });
        Function function = new Function("bTokenBalance", inputParam, outputParam);
        try {
            EthCall call = request(ConstantUtil.DEPOSIT_CONTRACT, function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
//                Log.e("MainActivity",""+result.get(0).getValue().toString());
                return result;
            }
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static List<Type> aTokenBalance() {
        List<Type> inputParam = new ArrayList<>();
        String address = DBWalletUtil.getCurrentWallet(MyApp.getsInstance()).address;
        inputParam.add(new Address(address));
        // Output parameters
        List<TypeReference<?>> outputParam = new ArrayList<>();
        outputParam.add(new TypeReference<Uint256>() {
        });
        Function function = new Function("aTokenBalance", inputParam, outputParam);
        try {
            EthCall call = request(ConstantUtil.DEPOSIT_CONTRACT, function);
            if (call.getError() == null) {
                List<Type> result = FunctionReturnDecoder.decode(call.getResult(), function.getOutputParameters());
//                Log.e("MainActivity",""+result.get(0).getValue().toString());
                return result;
            }
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

}
